library(ggplot2)
library(dplyr)
library(tidyr)
library(lubridate)
library(gridExtra)
library(stringr)

Exercise 1 - Community Mobility Open Data

• Community Mobility Reports have been created with the aim to provide insights into what has changed in response to policies aimed at combating COVID-19. Data can be found at https://www.google.com/covid19/mobility/

• Download and analyze the following data sets: - https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv and - https://www.gstatic.com/covid19/mobility/Region_Mobility_Report_CSVs.zip

The data show how visitors to (or time spent in) categorized places change compared to baseline days. A baseline day represents a normal value for that day of the week. The baseline day is the median value from the 5-week period Jan 3 – Feb 6, 2020.

To make the reports useful, categories have been used to group some of the places with similar characteristics for purposes of social distancing guidance. The following categories are available:

• Select a couple of European countries of your choice and analyze the trends in the previous variables over time:

df_glob <- read.csv('Global_Mobility_Report.csv')
df_glob$week <- week(df_glob$date)
df_glob$month <- month(df_glob$date)

col <- colnames(df_glob)
observables <- names(df_glob)[10:(length(col)-2)] #have to remove week and month                                                  columns added selecting the features 

countries <- c('Italy', 'France', 'Portugal')

pl_week <- function(feature){
  plot <- ggplot() + theme_linedraw() +
  geom_line(aes(x=ita_weekly$week, y=ita_weekly[[feature]], color='Italy'), lwd=1.8) +
  geom_line(aes(x=fra_weekly$week, y=fra_weekly[[feature]], color='France'),lwd=1.8) +
geom_line(aes(x=por_weekly$week, y=por_weekly[[feature]], color='Portugal'),lwd=1.8) +
    labs(title=sprintf('Italy, France and Portugal -  Week comparison on %s', feature), color='Legend', x='Week', y='Precentual change') +
    theme(plot.title = element_text(size=45),
      axis.text.x = element_text(size=25),
      axis.text.y = element_text(size=25),
      axis.title.x = element_text(size=40),
      axis.title.y = element_text(size=40),
      axis.ticks.length=unit(.6, "cm"),
      legend.text=element_text(size=35)) +                                             scale_x_continuous(breaks=1:53, labels=1:53) 
  
return(plot)
}


df_it <- df_glob[df_glob$country_region == countries[1],]
df_fr <- df_glob[df_glob$country_region == countries[2],]
df_por <- df_glob[df_glob$country_region == countries[3],]

for (obs in observables){
  ita_weekly <- summarise_at(group_by(df_it,week) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  fra_weekly <- summarise_at(group_by(df_fr,week) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  por_weekly <- summarise_at(group_by(df_por,week) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  
  print(pl_week(feature = obs))
}

Almost in all the plotted features/activities the virus forced a decreasing, while all the growths in these curves correspond to the re-openings in the periods when the Covid-19 was in reduction.


pl_month <- function(feature){

  plot <- ggplot() + theme_linedraw() +
     geom_line(aes(x=ita_monthly$month, y=ita_monthly[[feature]], 
color='Italy'), lwd=1.5) +
     geom_line(aes(x=fra_monthly$month, y=fra_monthly[[feature]], color='France'),lwd=1.5) +
    geom_line(aes(x=por_monthly$month, y=por_monthly[[feature]], color='Portugal'),lwd=1.5) +
    
    labs(title=sprintf('Italy, France and Portugal - Month comparison on %s', feature), color='Legend', x='Month', y='Precentual change') +
    
    theme(plot.title = element_text(size=45),
      axis.text.x = element_text(size=25),
      axis.text.y = element_text(size=25),
      axis.title.x = element_text(size=40),
      axis.title.y = element_text(size=40),
      axis.ticks.length=unit(.6, "cm"),
      legend.text=element_text(size=35)) +                                             scale_x_continuous(breaks=1:12, labels=1:12)
  
return(plot)
}

for (obs in observables){
  ita_monthly <- summarise_at(group_by(df_it,month) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  fra_monthly <- summarise_at(group_by(df_fr,month) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  por_monthly <- summarise_at(group_by(df_por,month) , vars(obs) , funs(mean(. ,na.rm = TRUE) ))
  
  print(pl_month(feature=obs))
}

Also in the monthly comparison the situation it is the same: the growths in these curves correspond to the periods when the Covid-19 was in reduction.

Exercise 2 - Random number generators

• one of the first random number generator was proposed by von Neumann, the so-called middle square algorithm

• write R code to implement this type of generator and, given a fixed digit number input, square it an remove the leading and trailing digits, in order to return a number with the same number of digits as the original number

• Suggestion : after having squared the number, convert it to a list of characters (number <- unlist(strsplit(as.character(x.squared),"“))) and, after having removed the head and tail of the list, convert it back to a number (as.numeric(paste(number.after.trimming, collapse=”")))

n_num <- readline(prompt="Insert how many numbers you have to generate ")
5

repeat{
rand_num <- readline(prompt="Insert the seed of the generation (with almost 10 digits): ")
rand_num <- as.numeric(rand_num)

in_list <- unlist(strsplit(as.character(rand_num),""))
if (length(in_list) < 10){cat("\n ERROR: The inserted number has NOT almost 10 digits:  ")}
else{ break}
  
}
56765

 ERROR: The inserted number has NOT almost 10 digits:  
4567875921312

results_df <- data.frame(0)
colnames(results_df) <- c("Random_Number")

for (n in seq(1:n_num)){
  
  sq_num <- rand_num^2
  num_list <- unlist(strsplit(as.character(sq_num),""))
  in_list <- unlist(strsplit(as.character(rand_num),""))
  
  #trimming phase

  x <- (num_list[ (ceiling((length(num_list) - length(in_list))/2) + 1):(length(num_list) - floor((length(num_list) - length(in_list))/2)) ] )
  
  #Have to shift to the left if the number starts with a 0
    i <- 1
    while (x[1] == 0){
      x <- (num_list[ (ceiling((length(num_list) - length(in_list))/2) + 1 - i):(length(num_list) - floor((length(num_list) - length(in_list))/2)-i)])
        i <- i+1

    }
  
  
  rand_num <- as.numeric(paste(x, collapse=""))
    
    results_df[n,1] = rand_num
}

results_df
NA
NA
NA
NA
NA
NA
NA

Exercise 3 - Bayesian Inference

• A publishing company has recently launched a new journal. In order to determine how effective it is in reaching its possible audience, a market survey company selects a random sample of people from a possible target audience and interviews them.

Out of 150 interviewed people, 29 have read the last issue of the journal.

  1. What kind of distribution would you assume for y, the number of people that have seen the last issue of the journal ?

  2. Assuming a uniform prior, what is the posterior distribution for y

  3. Plot both posterior and likelihood distributions functions

For instance I can assume a Binomial distribution for \(y\), where the last issue is read with probability \(p\) and \(1 - p\) for the opposite case. Considering \(N\) interviewed people, I am looking to \(k\) people that read the last issue in \(N\) Bernoulli trials, i.e. a likelihood like:

\[ P(y|N,p,M) = {N \choose y} \cdot p^y \cdot(1-p)^{n-y} \] If I assuming a Uniform Prior \(U(0,1)\) the Posterior would be proportional to the previous likelihood (\(Z\) is called Evidence)

\[ P(y|N,p,M) = \frac{ {N \choose y} \cdot p^y \cdot(1-p)^{n-y}}{Z} \]


N <- 150  #n_interviews
y <- 29  #n_reads

n_data <- 1000
p = seq(0,1, length.out = n_data)


#likelihood

lk <- dbinom(y,N,p)

plot1 <- ggplot() + theme_linedraw() + 
       geom_line(aes(x=p, y=lk), color='darkorange1', lwd=1.5) +
        ggtitle("Likelihood Function")+ labs( x='p', y='Probability Density') +                     theme(plot.title = element_text(size=16), 
                  axis.text.x = element_text(size=10), 
                  axis.text.y = element_text(size=10),
                  axis.title.x = element_text(size=12),
                  axis.title.y = element_text(size=12))

plot1


#posterior with uniform prior
post = lk*1

#have to normalized
posterior = post/sum(post/n_data)



plot2 <- ggplot() + theme_linedraw() + 
       geom_line(aes(x=p, y=posterior), color='steelblue3', lwd=1.5) +
        ggtitle("Posterior Function")+ labs( x='p', y='Probability Density') +                     theme(plot.title = element_text(size=16), 
                  axis.text.x = element_text(size=10), 
                  axis.text.y = element_text(size=10),
                  axis.title.x = element_text(size=12),
                  axis.title.y = element_text(size=12))

plot2

NA
NA

Exercise 4 - Bayesian Inference

• A coin is flipped n = 30 times with the following outcomes: T, T, T, T, T, H, T, T, H, H, T, T, H, H, H, T, H, T, H, T, H, H, T, H, T, H, T, H, H, H

  1. Assuming a flat prior, and a beta prior, plot the likelihood, prior and posterior distributions for the data set.

  2. Evaluate the most probable value for the coin probability p and, integrating the posterior probability distribution, give an estimate for a 95% credibility interval.

  3. Repeat the same analysis assuming a sequential analysis of the data. Show how the most probable value and the credibility interval change as a function of the number of coin tosses (i.e. from 1 to 30).

  4. Do you get a different result, by analyzing the data sequentially with respect to a one-step analysis (i.e. considering all the data as a whole)?


outcomes <- c('T', 'T', 'T', 'T', 'T', 'H', 'T', 'T', 'H', 'H', 'T', 'T', 'H', 'H', 'H', 'T', 'H', 'T', 'H', 'T', 'H', 'H', 'T', 'H', 'T', 'H', 'T', 'H', 'H', 'H')

n_times <- 30
h <- 15  #also with str_count
t <- n_times - h

  
n_data <- 1000
p = seq(0,1, length.out = n_data)

#priors
alpha <- 50
beta <- 50

flat_prior <- rep(1,n_data)
beta_prior <- dbeta(p, alpha , beta)


#likelihood coin toss --> binomial distribution
lk <- dbinom(h,n_times,p)


#posteriors
post1 <- lk*1
flat_post <- post1/sum(post1/n_data)

post2 <- lk*beta_prior
beta_post <- post2/sum(post2/n_data)


df = data.frame(
    x          = c(p, p),
    prior      = c(flat_prior, beta_prior),
    likelihood = c(lk, lk),
    posterior  = c(flat_post, beta_post),
    label      = c(rep("Flat", n_data), rep("Beta", n_data))
)

plot_pr <- ggplot(data= df , aes(x = x, y=prior)) + theme_linedraw() + 
       geom_line(aes(color=label), lwd=1.5) +
        ggtitle("Prior Functions")+ labs( x='p', y='Probability Density') +
         theme(plot.title = element_text(size=30), 
                                 axis.text.x = element_text(size=20), 
                                 axis.text.y = element_text(size=20),
                                 axis.title.x = element_text(size=22),
                                 axis.title.y = element_text(size=22),
                                 legend.text=element_text(size=20),
                                 legend.title=element_text(size=20))

plot_lk <- ggplot(data= df , aes(x = x, y=likelihood)) + theme_linedraw() + 
       geom_line(aes(color=label), lwd=1.5) +
        ggtitle("Likelihood Functions")+ labs( x='p', y='Probability Density') +
         theme(plot.title = element_text(size=30), 
                                 axis.text.x = element_text(size=20), 
                                 axis.text.y = element_text(size=20),
                                 axis.title.x = element_text(size=22),
                                 axis.title.y = element_text(size=22),
                                 legend.text = element_text(size=20),
                                 legend.title = element_text(size=20))


plot_post <- ggplot(data= df , aes(x = x, y=posterior)) + theme_linedraw() + 
       geom_line(aes(color=label), lwd=1.5) +
        ggtitle("Posterior Functions")+ labs( x='p', y='Probability Density') +
         theme(plot.title = element_text(size=30), 
                                 axis.text.x = element_text(size=20), 
                                 axis.text.y = element_text(size=20),
                                 axis.title.x = element_text(size=22),
                                 axis.title.y = element_text(size=22),
                                 legend.text = element_text(size=20),
                                 legend.title = element_text(size=20))



grid.arrange(plot_pr, plot_lk, plot_post, ncol=3)

most_prob_flat <- p[which.max(flat_post)]
most_prob_beta <- p[which.max(beta_post)]

cat("The Most Probable Value for the Coin Toss probability p is:\n 
    \t", most_prob_flat, "with the flat prior \n
    \t", most_prob_beta, "with the beta prior \n")
The Most Probable Value for the Coin Toss probability p is:
 
         0.4994995 with the flat prior 

         0.4994995 with the beta prior 
#95 % credibility interval

flat_l_lim <- p[cumsum(flat_post/n_data) > 0.025 ][1]
flat_h_lim <- p[cumsum(flat_post/n_data) > 0.975 ][1]

beta_l_lim <- p[cumsum(beta_post/n_data) > 0.025 ][1]
beta_h_lim <- p[cumsum(beta_post/n_data) > 0.975 ][1]


cat("\n95 % Credibility Interval :\n
    \t", flat_l_lim, "-", flat_h_lim, "with the flat prior \n
    \t", beta_l_lim, "-", beta_h_lim, "with the beta prior \n")

95 % Credibility Interval :

         0.3303303 - 0.6696697 with the flat prior 

         0.4144144 - 0.5855856 with the beta prior 

x_range <- seq(flat_l_lim,flat_h_lim, length.out = 200)


plot_CI1 <- ggplot() + theme_linedraw() +   
  geom_line(aes(x = p, y=flat_post), color='steelblue3', lwd=1.5) +
                geom_vline(xintercept = flat_l_lim , linetype='dashed', lwd=0.8) +
                geom_vline(xintercept = flat_h_lim , linetype='dashed', lwd=0.8) +
geom_area(aes(x_range, y=dbeta(x=x_range, 1+15, 1+30-15)), alpha =0.3, fill = 'coral1') +

  ggtitle("Posterior Function (from Flat Prior) with 95% Credibility Interval")+              
          labs(x='p', y='Probability Density') +
                theme(plot.title = element_text(size=15), 
                                 axis.text.x = element_text(size=10), 
                                 axis.text.y = element_text(size=10),
                                 axis.title.x = element_text(size=12),
                                 axis.title.y = element_text(size=12))

plot_CI1


#geom_area(aes(x=range95, y=)

x_range_b <- seq(beta_l_lim, beta_h_lim, length.out = 200)

plot_CI2 <- ggplot() + theme_linedraw() +   
  geom_line(aes(x = p, y=beta_post), color='firebrick3', lwd=1.5) +
            geom_vline(xintercept = beta_l_lim , linetype='dashed', lwd=0.8) +
            geom_vline(xintercept = beta_h_lim , linetype='dashed', lwd=0.8) +
           geom_area(aes(x_range_b, y=dbeta(x=x_range_b, 50+15, 50+30-15)), alpha =0.3, fill = 'coral1') +

   ggtitle("Posterior Function (from Beta Prior) with 95% Credibility Interval") +  
    labs(x='p', y='Probability Density') +
         theme(plot.title = element_text(size=15), 
                                 axis.text.x = element_text(size=10), 
                                 axis.text.y = element_text(size=10),
                                 axis.title.x = element_text(size=12),
                                 axis.title.y = element_text(size=12))

plot_CI2

NA
NA
NA


#sequantial analysis
n_times <- 30
n_data <- 1000

p = seq(0,1, length.out = n_data)

#priors
alpha <- 50
beta <- 50
flat_prior <- rep(1,n_data)
beta_prior <- dbeta(p, alpha , beta)

most_prob_flat <- c()
most_prob_beta <- c()
flat_l_lim  <- c()
flat_u_lim  <- c()
beta_l_lim  <- c()
beta_u_lim  <- c()

n_in <- 1

for(i in 1:n_times){
  
    h <- sum(str_count(outcomes[i], 'H'))

    #likelihood coin toss --> binomial distribution
    lk <- dbinom(h,n_in,p)
    
    #posteriors
    post1 <- lk*flat_prior
    flat_post <- post1/sum(post1/n_data)
    
    post2 <- lk*beta_prior
    beta_post <- post2/sum(post2/n_data)

    #sequential <-> update all at each step
    most_prob_flat = c(most_prob_flat, p[which.max(flat_post)])
    most_prob_beta = c(most_prob_beta, p[which.max(beta_post)])
    flat_l_lim   = c(flat_l_lim,  p[cumsum(flat_post/n_data) > 0.025][1])
    flat_u_lim  = c(flat_u_lim, p[cumsum(flat_post/n_data) > 0.975][1])
    beta_l_lim   = c(beta_l_lim,  p[cumsum(beta_post/n_data) > 0.025][1])
    beta_u_lim  = c(beta_u_lim, p[cumsum(beta_post/n_data) > 0.975][1])

    #and use the found posteriors as priors for the next step 
    flat_prior = flat_post
    beta_prior = beta_post
    
}

df_seq = data.frame(
    x          = c(p, p),
    posterior  = c(flat_post, beta_post),
    label      = c(rep("Uniform", n_data), rep("Beta", n_data))
)


plot_post_s <- ggplot(data=df_seq , aes(x=x, y=posterior)) + theme_linedraw() +   
  geom_line( aes(color=label), lwd=1.5) +
      ggtitle("Posterior Functions - Sequential Analysis")+              
          labs(x='p', y='Probability Density') +
                theme(plot.title = element_text(size=15), 
                                 axis.text.x = element_text(size=10), 
                                 axis.text.y = element_text(size=10),
                                 axis.title.x = element_text(size=12),
                                 axis.title.y = element_text(size=12),
                                  legend.text = element_text(size=12),
                                 legend.title = element_text(size=12))

plot_post_s


df_CI = data.frame(
    x = rep(1:n_times, 2),
    y = c(most_prob_flat, most_prob_beta),
    y_max = c(flat_u_lim, beta_u_lim),
    y_min = c(flat_l_lim, beta_l_lim),
    label = c(rep("Flat", n_times), rep("Beta", n_times))
)

plot_CI3 <- ggplot(data=df_CI, aes(x=x, y=y, ymax=y_max, min=y_min, fill=label, linetype=label, color=label)) +   
     theme_linedraw() +                                            
     geom_line(aes(color=label), lwd=1.5) +    
     geom_ribbon(alpha=0.3) + 
  
      ggtitle("Summary plot - sequential analysis") +
      labs(xlab='Step', ylab='Most Probable Value for p',
           subtitle="Filled regions represent the 95% credibility interval") +
  
      theme(plot.title = element_text(size=22),
            plot.subtitle = element_text(size=12), 
             axis.text.x = element_text(size=10), 
             axis.text.y = element_text(size=10),
             axis.title.x = element_text(size=12),
             axis.title.y = element_text(size=12),
            legend.text = element_text(size=12),
            legend.title = element_text(size=12))
                                         

plot_CI3

NA
NA
LS0tCnRpdGxlOiAiUiBMYWJvcmF0b3J5IDQiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3J9CmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KHN0cmluZ3IpCgpgYGAKCgojIyBFeGVyY2lzZSAxIC0gQ29tbXVuaXR5IE1vYmlsaXR5IE9wZW4gRGF0YQoK4oCiIENvbW11bml0eSBNb2JpbGl0eSBSZXBvcnRzIGhhdmUgYmVlbiBjcmVhdGVkIHdpdGggdGhlIGFpbSB0byBwcm92aWRlIGluc2lnaHRzIAppbnRvIHdoYXQgaGFzIGNoYW5nZWQgaW4gcmVzcG9uc2UgdG8gcG9saWNpZXMgYWltZWQgYXQgY29tYmF0aW5nIENPVklELTE5LgpEYXRhIGNhbiBiZSBmb3VuZCBhdCBodHRwczovL3d3dy5nb29nbGUuY29tL2NvdmlkMTkvbW9iaWxpdHkvCgrigKIgRG93bmxvYWQgYW5kIGFuYWx5emUgdGhlIGZvbGxvd2luZyBkYXRhIHNldHM6Ci0gaHR0cHM6Ly93d3cuZ3N0YXRpYy5jb20vY292aWQxOS9tb2JpbGl0eS9HbG9iYWxfTW9iaWxpdHlfUmVwb3J0LmNzdgphbmQKLSBodHRwczovL3d3dy5nc3RhdGljLmNvbS9jb3ZpZDE5L21vYmlsaXR5L1JlZ2lvbl9Nb2JpbGl0eV9SZXBvcnRfQ1NWcy56aXAKClRoZSBkYXRhIHNob3cgaG93IHZpc2l0b3JzIHRvIChvciB0aW1lIHNwZW50IGluKSBjYXRlZ29yaXplZCBwbGFjZXMgY2hhbmdlIApjb21wYXJlZCB0byBiYXNlbGluZSBkYXlzLiBBIGJhc2VsaW5lIGRheSByZXByZXNlbnRzIGEgbm9ybWFsIHZhbHVlIGZvcgp0aGF0IGRheSBvZiB0aGUgd2Vlay4gVGhlIGJhc2VsaW5lIGRheSBpcyB0aGUgbWVkaWFuIHZhbHVlIGZyb20gdGhlIAo1LXdlZWsgcGVyaW9kIEphbiAzIOKAkyBGZWIgNiwgMjAyMC4KClRvIG1ha2UgdGhlIHJlcG9ydHMgdXNlZnVsLCBjYXRlZ29yaWVzIGhhdmUgYmVlbiB1c2VkIHRvIGdyb3VwIHNvbWUgCm9mIHRoZSBwbGFjZXMgd2l0aCBzaW1pbGFyIGNoYXJhY3RlcmlzdGljcyBmb3IgcHVycG9zZXMgb2Ygc29jaWFsCmRpc3RhbmNpbmcgZ3VpZGFuY2UuIFRoZSBmb2xsb3dpbmcgY2F0ZWdvcmllcyBhcmUgYXZhaWxhYmxlOgoKLSByZXRhaWwgYW5kIHJlY3JlYXRpb24sIGkuZS4gcGxhY2VzIGxpa2UgcmVzdGF1cmFudHMsIGNhZmVzLCBzaG9wcGluZyBjZW50ZXJzLAp0aGVtZSBwYXJrcyxtdXNldW1zLCBsaWJyYXJpZXMsIGFuZCBtb3ZpZSB0aGVhdGVycwoKLSBncm9jZXJ5IGFuZCBwaGFybWFjeSwgaS5lLiBncm9jZXJ5IG1hcmtldHMsIGZvb2Qgd2FyZWhvdXNlcywgZmFybWVycyBtYXJrZXRzLCAKc3BlY2lhbHR5IGZvb2Qgc2hvcHMsIGRydWcgc3RvcmVzLCBhbmQgcGhhcm1hY2llcwoKLSBwYXJrcywgaS5lLiBuYXRpb25hbCBwYXJrcywgcHVibGljIGJlYWNoZXMsIG1hcmluYXMsIGRvZyBwYXJrcywgCnBsYXphcywgYW5kIHB1YmxpYyBnYXJkZW5zIAoKLSB0cmFuc2l0IHN0YXRpb25zIGkuZS4gYWxsIHB1YmxpYyB0cmFuc3BvcnQgaHVicyBzdWNoIGFzIHN1YndheSwgYnVzLAphbmQgdHJhaW4gc3RhdGlvbnMgCgotIHdvcmtwbGFjZXMsIGkuZS4gcGxhY2VzIG9mIHdvcmsKCi0gcmVzaWRlbnRpYWwsIGkuZS4gcGVvcGxl4oCZcyByZXNpZGVuY2UKCuKAoiBTZWxlY3QgYSBjb3VwbGUgb2YgRXVyb3BlYW4gY291bnRyaWVzIG9mIHlvdXIgY2hvaWNlIGFuZCBhbmFseXplIHRoZSB0cmVuZHMgCmluIHRoZSBwcmV2aW91cyB2YXJpYWJsZXMgb3ZlciB0aW1lOgoKLSBwcm9kdWNlIGEgcGxvdCBvZiB0aGUgZGF0YSBieSBhdmVyYWdpbmcgdGhlIG9ic2VydmFibGUgb3ZlciBhIHBlcmlvZCBvZiBvbmUKd2VlayAoaGludDogY29udmVydCB0aGUgZGF0YSBmaWVsZCB0byBsdWJyaWRhdGU6OndlZWspIGFuZCBvbmUgbW9udGggYW5kIApxdWFudGlmeSB0aGUgaW1wYWN0IG9mIENPVklELSAxOSByZXN0cmljdGlvbnMgb24gbW9iaWxpdHkgc2l0dWF0aW9ucy4KCgpgYGB7cn0KZGZfZ2xvYiA8LSByZWFkLmNzdignR2xvYmFsX01vYmlsaXR5X1JlcG9ydC5jc3YnKQpkZl9nbG9iJHdlZWsgPC0gd2VlayhkZl9nbG9iJGRhdGUpCmRmX2dsb2IkbW9udGggPC0gbW9udGgoZGZfZ2xvYiRkYXRlKQpgYGAKCgoKCmBgYHtyIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLndpZHRoPTM1ICwgZmlnLmhlaWdodD0xMn0KCmNvbCA8LSBjb2xuYW1lcyhkZl9nbG9iKQpvYnNlcnZhYmxlcyA8LSBuYW1lcyhkZl9nbG9iKVsxMDoobGVuZ3RoKGNvbCktMildICNoYXZlIHRvIHJlbW92ZSB3ZWVrIGFuZCBtb250aCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1ucyBhZGRlZCBzZWxlY3RpbmcgdGhlIGZlYXR1cmVzIAoKY291bnRyaWVzIDwtIGMoJ0l0YWx5JywgJ0ZyYW5jZScsICdQb3J0dWdhbCcpCgpwbF93ZWVrIDwtIGZ1bmN0aW9uKGZlYXR1cmUpewogIHBsb3QgPC0gZ2dwbG90KCkgKyB0aGVtZV9saW5lZHJhdygpICsKICBnZW9tX2xpbmUoYWVzKHg9aXRhX3dlZWtseSR3ZWVrLCB5PWl0YV93ZWVrbHlbW2ZlYXR1cmVdXSwgY29sb3I9J0l0YWx5JyksIGx3ZD0xLjgpICsKICBnZW9tX2xpbmUoYWVzKHg9ZnJhX3dlZWtseSR3ZWVrLCB5PWZyYV93ZWVrbHlbW2ZlYXR1cmVdXSwgY29sb3I9J0ZyYW5jZScpLGx3ZD0xLjgpICsKZ2VvbV9saW5lKGFlcyh4PXBvcl93ZWVrbHkkd2VlaywgeT1wb3Jfd2Vla2x5W1tmZWF0dXJlXV0sIGNvbG9yPSdQb3J0dWdhbCcpLGx3ZD0xLjgpICsKICAgIGxhYnModGl0bGU9c3ByaW50ZignSXRhbHksIEZyYW5jZSBhbmQgUG9ydHVnYWwgLSAgV2VlayBjb21wYXJpc29uIG9uICVzJywgZmVhdHVyZSksIGNvbG9yPSdMZWdlbmQnLCB4PSdXZWVrJywgeT0nUHJlY2VudHVhbCBjaGFuZ2UnKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NDUpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTI1KSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yNSksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTQwKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9NDApLAogICAgICBheGlzLnRpY2tzLmxlbmd0aD11bml0KC42LCAiY20iKSwKICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MzUpKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz0xOjUzLCBsYWJlbHM9MTo1MykgCiAgCnJldHVybihwbG90KQp9CgoKZGZfaXQgPC0gZGZfZ2xvYltkZl9nbG9iJGNvdW50cnlfcmVnaW9uID09IGNvdW50cmllc1sxXSxdCmRmX2ZyIDwtIGRmX2dsb2JbZGZfZ2xvYiRjb3VudHJ5X3JlZ2lvbiA9PSBjb3VudHJpZXNbMl0sXQpkZl9wb3IgPC0gZGZfZ2xvYltkZl9nbG9iJGNvdW50cnlfcmVnaW9uID09IGNvdW50cmllc1szXSxdCgpmb3IgKG9icyBpbiBvYnNlcnZhYmxlcyl7CiAgaXRhX3dlZWtseSA8LSBzdW1tYXJpc2VfYXQoZ3JvdXBfYnkoZGZfaXQsd2VlaykgLCB2YXJzKG9icykgLCBmdW5zKG1lYW4oLiAsbmEucm0gPSBUUlVFKSApKQogIGZyYV93ZWVrbHkgPC0gc3VtbWFyaXNlX2F0KGdyb3VwX2J5KGRmX2ZyLHdlZWspICwgdmFycyhvYnMpICwgZnVucyhtZWFuKC4gLG5hLnJtID0gVFJVRSkgKSkKICBwb3Jfd2Vla2x5IDwtIHN1bW1hcmlzZV9hdChncm91cF9ieShkZl9wb3Isd2VlaykgLCB2YXJzKG9icykgLCBmdW5zKG1lYW4oLiAsbmEucm0gPSBUUlVFKSApKQogIAogIHByaW50KHBsX3dlZWsoZmVhdHVyZSA9IG9icykpCn0KCmBgYApBbG1vc3QgaW4gYWxsIHRoZSBwbG90dGVkIGZlYXR1cmVzL2FjdGl2aXRpZXMgdGhlIHZpcnVzIGZvcmNlZCBhIGRlY3JlYXNpbmcsCndoaWxlIGFsbCB0aGUgZ3Jvd3RocyBpbiB0aGVzZSBjdXJ2ZXMgY29ycmVzcG9uZCB0byB0aGUgcmUtb3BlbmluZ3MgaW4gdGhlCnBlcmlvZHMgd2hlbiB0aGUgQ292aWQtMTkgd2FzIGluIHJlZHVjdGlvbi4KCgpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD0zNSAsIGZpZy5oZWlnaHQ9MTJ9CgpwbF9tb250aCA8LSBmdW5jdGlvbihmZWF0dXJlKXsKCiAgcGxvdCA8LSBnZ3Bsb3QoKSArIHRoZW1lX2xpbmVkcmF3KCkgKwogICAgIGdlb21fbGluZShhZXMoeD1pdGFfbW9udGhseSRtb250aCwgeT1pdGFfbW9udGhseVtbZmVhdHVyZV1dLCAKY29sb3I9J0l0YWx5JyksIGx3ZD0xLjUpICsKICAgICBnZW9tX2xpbmUoYWVzKHg9ZnJhX21vbnRobHkkbW9udGgsIHk9ZnJhX21vbnRobHlbW2ZlYXR1cmVdXSwgY29sb3I9J0ZyYW5jZScpLGx3ZD0xLjUpICsKICAgIGdlb21fbGluZShhZXMoeD1wb3JfbW9udGhseSRtb250aCwgeT1wb3JfbW9udGhseVtbZmVhdHVyZV1dLCBjb2xvcj0nUG9ydHVnYWwnKSxsd2Q9MS41KSArCiAgICAKICAgIGxhYnModGl0bGU9c3ByaW50ZignSXRhbHksIEZyYW5jZSBhbmQgUG9ydHVnYWwgLSBNb250aCBjb21wYXJpc29uIG9uICVzJywgZmVhdHVyZSksIGNvbG9yPSdMZWdlbmQnLCB4PSdNb250aCcsIHk9J1ByZWNlbnR1YWwgY2hhbmdlJykgKwogICAgCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NDUpLAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTI1KSwKICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yNSksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTQwKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9NDApLAogICAgICBheGlzLnRpY2tzLmxlbmd0aD11bml0KC42LCAiY20iKSwKICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MzUpKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz0xOjEyLCBsYWJlbHM9MToxMikKICAKcmV0dXJuKHBsb3QpCn0KCmZvciAob2JzIGluIG9ic2VydmFibGVzKXsKICBpdGFfbW9udGhseSA8LSBzdW1tYXJpc2VfYXQoZ3JvdXBfYnkoZGZfaXQsbW9udGgpICwgdmFycyhvYnMpICwgZnVucyhtZWFuKC4gLG5hLnJtID0gVFJVRSkgKSkKICBmcmFfbW9udGhseSA8LSBzdW1tYXJpc2VfYXQoZ3JvdXBfYnkoZGZfZnIsbW9udGgpICwgdmFycyhvYnMpICwgZnVucyhtZWFuKC4gLG5hLnJtID0gVFJVRSkgKSkKICBwb3JfbW9udGhseSA8LSBzdW1tYXJpc2VfYXQoZ3JvdXBfYnkoZGZfcG9yLG1vbnRoKSAsIHZhcnMob2JzKSAsIGZ1bnMobWVhbiguLG5hLnJtID0gVFJVRSkgKSkKICAKICBwcmludChwbF9tb250aChmZWF0dXJlPW9icykpCn0KCmBgYApBbHNvIGluIHRoZSBtb250aGx5IGNvbXBhcmlzb24gdGhlIHNpdHVhdGlvbiBpdCBpcyB0aGUgc2FtZTogdGhlIGdyb3d0aHMgaW4gdGhlc2UgY3VydmVzIGNvcnJlc3BvbmQgdG8gdGhlIHBlcmlvZHMgd2hlbiB0aGUgQ292aWQtMTkgd2FzIGluIHJlZHVjdGlvbi4KCgojIyBFeGVyY2lzZSAyIC0gUmFuZG9tIG51bWJlciBnZW5lcmF0b3JzCgoK4oCiIG9uZSBvZiB0aGUgZmlyc3QgcmFuZG9tIG51bWJlciBnZW5lcmF0b3Igd2FzIHByb3Bvc2VkIGJ5IHZvbiBOZXVtYW5uLCB0aGUKc28tY2FsbGVkIG1pZGRsZSBzcXVhcmUgYWxnb3JpdGhtCgrigKIgd3JpdGUgUiBjb2RlIHRvIGltcGxlbWVudCB0aGlzIHR5cGUgb2YgZ2VuZXJhdG9yIGFuZCwgZ2l2ZW4gYSBmaXhlZCAKZGlnaXQgbnVtYmVyIGlucHV0LCBzcXVhcmUgaXQgYW4gcmVtb3ZlIHRoZSBsZWFkaW5nIGFuZCB0cmFpbGluZyBkaWdpdHMsIAppbiBvcmRlciB0byByZXR1cm4gYSBudW1iZXIgd2l0aCB0aGUgc2FtZSBudW1iZXIgb2YgZGlnaXRzIGFzIHRoZSAKb3JpZ2luYWwgbnVtYmVyCgrigKIgU3VnZ2VzdGlvbiA6IGFmdGVyIGhhdmluZyBzcXVhcmVkIHRoZSBudW1iZXIsIGNvbnZlcnQgaXQgdG8gYSAKbGlzdCBvZiBjaGFyYWN0ZXJzIChudW1iZXIgPC0gdW5saXN0KHN0cnNwbGl0KGFzLmNoYXJhY3Rlcih4LnNxdWFyZWQpLCIiKSkpIGFuZCwKYWZ0ZXIgaGF2aW5nIApyZW1vdmVkIHRoZSBoZWFkIGFuZCB0YWlsIG9mIHRoZSBsaXN0LCBjb252ZXJ0IGl0IGJhY2sgdG8gYSBudW1iZXIKKGFzLm51bWVyaWMocGFzdGUobnVtYmVyLmFmdGVyLnRyaW1taW5nLCBjb2xsYXBzZT0iIikpKQoKYGBge3J9Cm5fbnVtIDwtIHJlYWRsaW5lKHByb21wdD0iSW5zZXJ0IGhvdyBtYW55IG51bWJlcnMgeW91IGhhdmUgdG8gZ2VuZXJhdGUgIikKCnJlcGVhdHsKcmFuZF9udW0gPC0gcmVhZGxpbmUocHJvbXB0PSJJbnNlcnQgdGhlIHNlZWQgb2YgdGhlIGdlbmVyYXRpb24gKHdpdGggYWxtb3N0IDEwIGRpZ2l0cyk6ICIpCnJhbmRfbnVtIDwtIGFzLm51bWVyaWMocmFuZF9udW0pCgppbl9saXN0IDwtIHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIocmFuZF9udW0pLCIiKSkKaWYgKGxlbmd0aChpbl9saXN0KSA8IDEwKXtjYXQoIlxuIEVSUk9SOiBUaGUgaW5zZXJ0ZWQgbnVtYmVyIGhhcyBOT1QgYWxtb3N0IDEwIGRpZ2l0czogICIpfQplbHNleyBicmVha30KICAKfQoKcmVzdWx0c19kZiA8LSBkYXRhLmZyYW1lKDApCmNvbG5hbWVzKHJlc3VsdHNfZGYpIDwtIGMoIlJhbmRvbV9OdW1iZXIiKQoKZm9yIChuIGluIHNlcSgxOm5fbnVtKSl7CiAgCiAgc3FfbnVtIDwtIHJhbmRfbnVtXjIKICBudW1fbGlzdCA8LSB1bmxpc3Qoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHNxX251bSksIiIpKQogIGluX2xpc3QgPC0gdW5saXN0KHN0cnNwbGl0KGFzLmNoYXJhY3RlcihyYW5kX251bSksIiIpKQogIAogICN0cmltbWluZyBwaGFzZQoKICB4IDwtIChudW1fbGlzdFsgKGNlaWxpbmcoKGxlbmd0aChudW1fbGlzdCkgLSBsZW5ndGgoaW5fbGlzdCkpLzIpICsgMSk6KGxlbmd0aChudW1fbGlzdCkgLSBmbG9vcigobGVuZ3RoKG51bV9saXN0KSAtIGxlbmd0aChpbl9saXN0KSkvMikpIF0gKQogIAogICNIYXZlIHRvIHNoaWZ0IHRvIHRoZSBsZWZ0IGlmIHRoZSBudW1iZXIgc3RhcnRzIHdpdGggYSAwCiAgICBpIDwtIDEKICAgIHdoaWxlICh4WzFdID09IDApewogICAgICB4IDwtIChudW1fbGlzdFsgKGNlaWxpbmcoKGxlbmd0aChudW1fbGlzdCkgLSBsZW5ndGgoaW5fbGlzdCkpLzIpICsgMSAtIGkpOihsZW5ndGgobnVtX2xpc3QpIC0gZmxvb3IoKGxlbmd0aChudW1fbGlzdCkgLSBsZW5ndGgoaW5fbGlzdCkpLzIpLWkpXSkKICAgICAgICBpIDwtIGkrMQoKICAgIH0KICAKICAKICByYW5kX251bSA8LSBhcy5udW1lcmljKHBhc3RlKHgsIGNvbGxhcHNlPSIiKSkKICAgIAogICAgcmVzdWx0c19kZltuLDFdID0gcmFuZF9udW0KfQoKcmVzdWx0c19kZgoKCgoKCgoKYGBgCgoKIyMgRXhlcmNpc2UgMyAtIEJheWVzaWFuIEluZmVyZW5jZQoK4oCiIEEgcHVibGlzaGluZyBjb21wYW55IGhhcyByZWNlbnRseSBsYXVuY2hlZCBhIG5ldyBqb3VybmFsLiBJbiBvcmRlcgp0byBkZXRlcm1pbmUgaG93IGVmZmVjdGl2ZSBpdCBpcyBpbiByZWFjaGluZyBpdHMgcG9zc2libGUgYXVkaWVuY2UsCmEgbWFya2V0IHN1cnZleSBjb21wYW55IHNlbGVjdHMgYSByYW5kb20gc2FtcGxlIG9mIHBlb3BsZSBmcm9tIGEgcG9zc2libGUKdGFyZ2V0IGF1ZGllbmNlIGFuZCBpbnRlcnZpZXdzIHRoZW0uIAoKT3V0IG9mIDE1MCBpbnRlcnZpZXdlZCBwZW9wbGUsIDI5IGhhdmUgcmVhZCB0aGUgbGFzdCBpc3N1ZSBvZiB0aGUgam91cm5hbC4KCmEpIFdoYXQga2luZCBvZiBkaXN0cmlidXRpb24gd291bGQgeW91IGFzc3VtZSBmb3IgeSwgdGhlIG51bWJlciBvZiBwZW9wbGUKdGhhdCBoYXZlIHNlZW4gdGhlIGxhc3QgaXNzdWUgb2YgdGhlIGpvdXJuYWwgPwoKYikgQXNzdW1pbmcgYSB1bmlmb3JtIHByaW9yLCB3aGF0IGlzIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uIGZvciB5CgpjKSBQbG90IGJvdGggcG9zdGVyaW9yIGFuZCBsaWtlbGlob29kIGRpc3RyaWJ1dGlvbnMgZnVuY3Rpb25zCgoKRm9yIGluc3RhbmNlIEkgY2FuIGFzc3VtZSBhIEJpbm9taWFsIGRpc3RyaWJ1dGlvbiBmb3IgJHkkLCAKd2hlcmUgdGhlIGxhc3QgaXNzdWUgaXMgcmVhZCB3aXRoIHByb2JhYmlsaXR5ICRwJCBhbmQgCiQxIC0gcCQgZm9yIHRoZSBvcHBvc2l0ZSBjYXNlLgpDb25zaWRlcmluZyAkTiQgaW50ZXJ2aWV3ZWQgcGVvcGxlLCBJIGFtIGxvb2tpbmcgdG8gJGskIHBlb3BsZSB0aGF0IHJlYWQgCnRoZSBsYXN0IGlzc3VlIGluICROJCBCZXJub3VsbGkgdHJpYWxzLCBpLmUuIGEgbGlrZWxpaG9vZCBsaWtlOgoKJCQKUCh5fE4scCxNKSA9IHtOIFxjaG9vc2UgeX0gXGNkb3QgcF55IFxjZG90KDEtcClee24teX0KJCQKSWYgSSBhc3N1bWluZyBhIFVuaWZvcm0gUHJpb3IgJFUoMCwxKSQgdGhlIFBvc3RlcmlvciB3b3VsZCBiZSBwcm9wb3J0aW9uYWwgdG8KdGhlIHByZXZpb3VzIGxpa2VsaWhvb2QgKCRaJCBpcyBjYWxsZWQgRXZpZGVuY2UpCgokJApQKHl8TixwLE0pID0gXGZyYWN7IHtOIFxjaG9vc2UgeX0gXGNkb3QgcF55IFxjZG90KDEtcClee24teX19e1p9CiQkCgoKYGBge3IgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcud2lkdGg9NyAsIGZpZy5oZWlnaHQ9NX0KCk4gPC0gMTUwICAjbl9pbnRlcnZpZXdzCnkgPC0gMjkgICNuX3JlYWRzCgpuX2RhdGEgPC0gMTAwMApwID0gc2VxKDAsMSwgbGVuZ3RoLm91dCA9IG5fZGF0YSkKCgojbGlrZWxpaG9vZAoKbGsgPC0gZGJpbm9tKHksTixwKQoKcGxvdDEgPC0gZ2dwbG90KCkgKyB0aGVtZV9saW5lZHJhdygpICsgCiAgICAgICBnZW9tX2xpbmUoYWVzKHg9cCwgeT1sayksIGNvbG9yPSdkYXJrb3JhbmdlMScsIGx3ZD0xLjUpICsKICAgICAgICBnZ3RpdGxlKCJMaWtlbGlob29kIEZ1bmN0aW9uIikrIGxhYnMoIHg9J3AnLCB5PSdQcm9iYWJpbGl0eSBEZW5zaXR5JykgKyAgICAgICAgICAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksIAogICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwgCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSkKCnBsb3QxCgojcG9zdGVyaW9yIHdpdGggdW5pZm9ybSBwcmlvcgpwb3N0ID0gbGsqMQoKI2hhdmUgdG8gbm9ybWFsaXplZApwb3N0ZXJpb3IgPSBwb3N0L3N1bShwb3N0L25fZGF0YSkKCgoKcGxvdDIgPC0gZ2dwbG90KCkgKyB0aGVtZV9saW5lZHJhdygpICsgCiAgICAgICBnZW9tX2xpbmUoYWVzKHg9cCwgeT1wb3N0ZXJpb3IpLCBjb2xvcj0nc3RlZWxibHVlMycsIGx3ZD0xLjUpICsKICAgICAgICBnZ3RpdGxlKCJQb3N0ZXJpb3IgRnVuY3Rpb24iKSsgbGFicyggeD0ncCcsIHk9J1Byb2JhYmlsaXR5IERlbnNpdHknKSArICAgICAgICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwgCiAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKcGxvdDIKCgpgYGAKCgojIyBFeGVyY2lzZSA0IC0gQmF5ZXNpYW4gSW5mZXJlbmNlCgrigKIgQSBjb2luIGlzIGZsaXBwZWQgbiA9IDMwIHRpbWVzIHdpdGggdGhlIGZvbGxvd2luZyBvdXRjb21lczoKVCwgVCwgVCwgVCwgVCwgSCwgVCwgVCwgSCwgSCwgVCwgVCwgSCwgSCwgSCwgVCwgSCwgVCwgSCwgVCwgSCwgSCwgVCwgSCwgVCwgSCwKVCwgSCwgSCwgSAoKYSkgQXNzdW1pbmcgYSBmbGF0IHByaW9yLCBhbmQgYSBiZXRhIHByaW9yLCBwbG90IHRoZSBsaWtlbGlob29kLCBwcmlvciBhbmQKcG9zdGVyaW9yIGRpc3RyaWJ1dGlvbnMgZm9yIHRoZSBkYXRhIHNldC4KCmIpIEV2YWx1YXRlIHRoZSBtb3N0IHByb2JhYmxlIHZhbHVlIGZvciB0aGUgY29pbiBwcm9iYWJpbGl0eSBwIGFuZCwgaW50ZWdyYXRpbmcgCnRoZSBwb3N0ZXJpb3IgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uLCBnaXZlIGFuIGVzdGltYXRlIApmb3IgYSA5NSUgY3JlZGliaWxpdHkgaW50ZXJ2YWwuCgpjKSBSZXBlYXQgdGhlIHNhbWUgYW5hbHlzaXMgYXNzdW1pbmcgYSBzZXF1ZW50aWFsIGFuYWx5c2lzIG9mIHRoZSBkYXRhLiAKU2hvdyBob3cgdGhlIG1vc3QgcHJvYmFibGUgdmFsdWUgYW5kIHRoZSBjcmVkaWJpbGl0eSBpbnRlcnZhbCBjaGFuZ2UgYXMgYSAKZnVuY3Rpb24gb2YgdGhlIG51bWJlciBvZiBjb2luIHRvc3NlcyAoaS5lLiBmcm9tIDEgdG8gMzApLgoKZCkgRG8geW91IGdldCBhIGRpZmZlcmVudCByZXN1bHQsIGJ5IGFuYWx5emluZyB0aGUgZGF0YSBzZXF1ZW50aWFsbHkKd2l0aCByZXNwZWN0IHRvIGEgb25lLXN0ZXAgYW5hbHlzaXMgKGkuZS4gY29uc2lkZXJpbmcgYWxsIHRoZSBkYXRhIGFzIGEgd2hvbGUpPwoKCgpgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD0yMCAsIGZpZy5oZWlnaHQ9N30KCm91dGNvbWVzIDwtIGMoJ1QnLCAnVCcsICdUJywgJ1QnLCAnVCcsICdIJywgJ1QnLCAnVCcsICdIJywgJ0gnLCAnVCcsICdUJywgJ0gnLCAnSCcsICdIJywgJ1QnLCAnSCcsICdUJywgJ0gnLCAnVCcsICdIJywgJ0gnLCAnVCcsICdIJywgJ1QnLCAnSCcsICdUJywgJ0gnLCAnSCcsICdIJykKCm5fdGltZXMgPC0gMzAKaCA8LSAxNSAgI2Fsc28gd2l0aCBzdHJfY291bnQKdCA8LSBuX3RpbWVzIC0gaAoKICAKbl9kYXRhIDwtIDEwMDAKcCA9IHNlcSgwLDEsIGxlbmd0aC5vdXQgPSBuX2RhdGEpCgojcHJpb3JzCmFscGhhIDwtIDUwCmJldGEgPC0gNTAKCmZsYXRfcHJpb3IgPC0gcmVwKDEsbl9kYXRhKQpiZXRhX3ByaW9yIDwtIGRiZXRhKHAsIGFscGhhICwgYmV0YSkKCgojbGlrZWxpaG9vZCBjb2luIHRvc3MgLS0+IGJpbm9taWFsIGRpc3RyaWJ1dGlvbgpsayA8LSBkYmlub20oaCxuX3RpbWVzLHApCgoKI3Bvc3RlcmlvcnMKcG9zdDEgPC0gbGsqMQpmbGF0X3Bvc3QgPC0gcG9zdDEvc3VtKHBvc3QxL25fZGF0YSkKCnBvc3QyIDwtIGxrKmJldGFfcHJpb3IKYmV0YV9wb3N0IDwtIHBvc3QyL3N1bShwb3N0Mi9uX2RhdGEpCgoKZGYgPSBkYXRhLmZyYW1lKAogICAgeCAgICAgICAgICA9IGMocCwgcCksCiAgICBwcmlvciAgICAgID0gYyhmbGF0X3ByaW9yLCBiZXRhX3ByaW9yKSwKICAgIGxpa2VsaWhvb2QgPSBjKGxrLCBsayksCiAgICBwb3N0ZXJpb3IgID0gYyhmbGF0X3Bvc3QsIGJldGFfcG9zdCksCiAgICBsYWJlbCAgICAgID0gYyhyZXAoIkZsYXQiLCBuX2RhdGEpLCByZXAoIkJldGEiLCBuX2RhdGEpKQopCgpwbG90X3ByIDwtIGdncGxvdChkYXRhPSBkZiAsIGFlcyh4ID0geCwgeT1wcmlvcikpICsgdGhlbWVfbGluZWRyYXcoKSArIAogICAgICAgZ2VvbV9saW5lKGFlcyhjb2xvcj1sYWJlbCksIGx3ZD0xLjUpICsKICAgICAgICBnZ3RpdGxlKCJQcmlvciBGdW5jdGlvbnMiKSsgbGFicyggeD0ncCcsIHk9J1Byb2JhYmlsaXR5IERlbnNpdHknKSArCiAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0zMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0yMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MjApKQoKcGxvdF9sayA8LSBnZ3Bsb3QoZGF0YT0gZGYgLCBhZXMoeCA9IHgsIHk9bGlrZWxpaG9vZCkpICsgdGhlbWVfbGluZWRyYXcoKSArIAogICAgICAgZ2VvbV9saW5lKGFlcyhjb2xvcj1sYWJlbCksIGx3ZD0xLjUpICsKICAgICAgICBnZ3RpdGxlKCJMaWtlbGlob29kIEZ1bmN0aW9ucyIpKyBsYWJzKCB4PSdwJywgeT0nUHJvYmFiaWxpdHkgRGVuc2l0eScpICsKICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTMwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MjIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjApKQoKCnBsb3RfcG9zdCA8LSBnZ3Bsb3QoZGF0YT0gZGYgLCBhZXMoeCA9IHgsIHk9cG9zdGVyaW9yKSkgKyB0aGVtZV9saW5lZHJhdygpICsgCiAgICAgICBnZW9tX2xpbmUoYWVzKGNvbG9yPWxhYmVsKSwgbHdkPTEuNSkgKwogICAgICAgIGdndGl0bGUoIlBvc3RlcmlvciBGdW5jdGlvbnMiKSsgbGFicyggeD0ncCcsIHk9J1Byb2JhYmlsaXR5IERlbnNpdHknKSArCiAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0zMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZT0yMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTIyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSkKCgoKZ3JpZC5hcnJhbmdlKHBsb3RfcHIsIHBsb3RfbGssIHBsb3RfcG9zdCwgbmNvbD0zKQpgYGAKCmBgYHtyfQptb3N0X3Byb2JfZmxhdCA8LSBwW3doaWNoLm1heChmbGF0X3Bvc3QpXQptb3N0X3Byb2JfYmV0YSA8LSBwW3doaWNoLm1heChiZXRhX3Bvc3QpXQoKY2F0KCJUaGUgTW9zdCBQcm9iYWJsZSBWYWx1ZSBmb3IgdGhlIENvaW4gVG9zcyBwcm9iYWJpbGl0eSBwIGlzOlxuIAogICAgXHQiLCBtb3N0X3Byb2JfZmxhdCwgIndpdGggdGhlIGZsYXQgcHJpb3IgXG4KICAgIFx0IiwgbW9zdF9wcm9iX2JldGEsICJ3aXRoIHRoZSBiZXRhIHByaW9yIFxuIikKCgojOTUgJSBjcmVkaWJpbGl0eSBpbnRlcnZhbAoKZmxhdF9sX2xpbSA8LSBwW2N1bXN1bShmbGF0X3Bvc3Qvbl9kYXRhKSA+IDAuMDI1IF1bMV0KZmxhdF9oX2xpbSA8LSBwW2N1bXN1bShmbGF0X3Bvc3Qvbl9kYXRhKSA+IDAuOTc1IF1bMV0KCmJldGFfbF9saW0gPC0gcFtjdW1zdW0oYmV0YV9wb3N0L25fZGF0YSkgPiAwLjAyNSBdWzFdCmJldGFfaF9saW0gPC0gcFtjdW1zdW0oYmV0YV9wb3N0L25fZGF0YSkgPiAwLjk3NSBdWzFdCgoKY2F0KCJcbjk1ICUgQ3JlZGliaWxpdHkgSW50ZXJ2YWwgOlxuCiAgICBcdCIsIGZsYXRfbF9saW0sICItIiwgZmxhdF9oX2xpbSwgIndpdGggdGhlIGZsYXQgcHJpb3IgXG4KICAgIFx0IiwgYmV0YV9sX2xpbSwgIi0iLCBiZXRhX2hfbGltLCAid2l0aCB0aGUgYmV0YSBwcmlvciBcbiIpCgoKYGBgCmBgYHtyIGZpZy5hbGlnbj0iY2VudGVyIiwgZmlnLndpZHRoPTcgLCBmaWcuaGVpZ2h0PTV9Cgp4X3JhbmdlIDwtIHNlcShmbGF0X2xfbGltLGZsYXRfaF9saW0sIGxlbmd0aC5vdXQgPSAyMDApCgoKcGxvdF9DSTEgPC0gZ2dwbG90KCkgKyB0aGVtZV9saW5lZHJhdygpICsgICAKICBnZW9tX2xpbmUoYWVzKHggPSBwLCB5PWZsYXRfcG9zdCksIGNvbG9yPSdzdGVlbGJsdWUzJywgbHdkPTEuNSkgKwogICAgICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gZmxhdF9sX2xpbSAsIGxpbmV0eXBlPSdkYXNoZWQnLCBsd2Q9MC44KSArCiAgICAgICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBmbGF0X2hfbGltICwgbGluZXR5cGU9J2Rhc2hlZCcsIGx3ZD0wLjgpICsKZ2VvbV9hcmVhKGFlcyh4X3JhbmdlLCB5PWRiZXRhKHg9eF9yYW5nZSwgMSsxNSwgMSszMC0xNSkpLCBhbHBoYSA9MC4zLCBmaWxsID0gJ2NvcmFsMScpICsKCiAgZ2d0aXRsZSgiUG9zdGVyaW9yIEZ1bmN0aW9uIChmcm9tIEZsYXQgUHJpb3IpIHdpdGggOTUlIENyZWRpYmlsaXR5IEludGVydmFsIikrICAgICAgICAgICAgICAKICAgICAgICAgIGxhYnMoeD0ncCcsIHk9J1Byb2JhYmlsaXR5IERlbnNpdHknKSArCiAgICAgICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMikpCgpwbG90X0NJMQoKCmBgYApgYGB7ciBmaWcuYWxpZ249ImNlbnRlciIsIGZpZy53aWR0aD03ICwgZmlnLmhlaWdodD01fQoKeF9yYW5nZV9iIDwtIHNlcShiZXRhX2xfbGltLCBiZXRhX2hfbGltLCBsZW5ndGgub3V0ID0gMjAwKQoKcGxvdF9DSTIgPC0gZ2dwbG90KCkgKyB0aGVtZV9saW5lZHJhdygpICsgICAKICBnZW9tX2xpbmUoYWVzKHggPSBwLCB5PWJldGFfcG9zdCksIGNvbG9yPSdmaXJlYnJpY2szJywgbHdkPTEuNSkgKwogICAgICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBiZXRhX2xfbGltICwgbGluZXR5cGU9J2Rhc2hlZCcsIGx3ZD0wLjgpICsKICAgICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYmV0YV9oX2xpbSAsIGxpbmV0eXBlPSdkYXNoZWQnLCBsd2Q9MC44KSArCiAgICAgICAgICAgZ2VvbV9hcmVhKGFlcyh4X3JhbmdlX2IsIHk9ZGJldGEoeD14X3JhbmdlX2IsIDUwKzE1LCA1MCszMC0xNSkpLCBhbHBoYSA9MC4zLCBmaWxsID0gJ2NvcmFsMScpICsKCiAgIGdndGl0bGUoIlBvc3RlcmlvciBGdW5jdGlvbiAoZnJvbSBCZXRhIFByaW9yKSB3aXRoIDk1JSBDcmVkaWJpbGl0eSBJbnRlcnZhbCIpICsgIAogICAgbGFicyh4PSdwJywgeT0nUHJvYmFiaWxpdHkgRGVuc2l0eScpICsKICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTE1KSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKcGxvdF9DSTIKCgoKYGBgCmBgYHtyfQoKCiNzZXF1YW50aWFsIGFuYWx5c2lzCm5fdGltZXMgPC0gMzAKbl9kYXRhIDwtIDEwMDAKCnAgPSBzZXEoMCwxLCBsZW5ndGgub3V0ID0gbl9kYXRhKQoKI3ByaW9ycwphbHBoYSA8LSA1MApiZXRhIDwtIDUwCmZsYXRfcHJpb3IgPC0gcmVwKDEsbl9kYXRhKQpiZXRhX3ByaW9yIDwtIGRiZXRhKHAsIGFscGhhICwgYmV0YSkKCm1vc3RfcHJvYl9mbGF0IDwtIGMoKQptb3N0X3Byb2JfYmV0YSA8LSBjKCkKZmxhdF9sX2xpbSAgPC0gYygpCmZsYXRfdV9saW0gIDwtIGMoKQpiZXRhX2xfbGltICA8LSBjKCkKYmV0YV91X2xpbSAgPC0gYygpCgpuX2luIDwtIDEKCmZvcihpIGluIDE6bl90aW1lcyl7CiAgCiAgICBoIDwtIHN1bShzdHJfY291bnQob3V0Y29tZXNbaV0sICdIJykpCgogICAgI2xpa2VsaWhvb2QgY29pbiB0b3NzIC0tPiBiaW5vbWlhbCBkaXN0cmlidXRpb24KICAgIGxrIDwtIGRiaW5vbShoLG5faW4scCkKICAgIAogICAgI3Bvc3RlcmlvcnMKICAgIHBvc3QxIDwtIGxrKmZsYXRfcHJpb3IKICAgIGZsYXRfcG9zdCA8LSBwb3N0MS9zdW0ocG9zdDEvbl9kYXRhKQogICAgCiAgICBwb3N0MiA8LSBsaypiZXRhX3ByaW9yCiAgICBiZXRhX3Bvc3QgPC0gcG9zdDIvc3VtKHBvc3QyL25fZGF0YSkKCiAgICAjc2VxdWVudGlhbCA8LT4gdXBkYXRlIGFsbCBhdCBlYWNoIHN0ZXAKICAgIG1vc3RfcHJvYl9mbGF0ID0gYyhtb3N0X3Byb2JfZmxhdCwgcFt3aGljaC5tYXgoZmxhdF9wb3N0KV0pCiAgICBtb3N0X3Byb2JfYmV0YSA9IGMobW9zdF9wcm9iX2JldGEsIHBbd2hpY2gubWF4KGJldGFfcG9zdCldKQogICAgZmxhdF9sX2xpbSAgID0gYyhmbGF0X2xfbGltLCAgcFtjdW1zdW0oZmxhdF9wb3N0L25fZGF0YSkgPiAwLjAyNV1bMV0pCiAgICBmbGF0X3VfbGltICA9IGMoZmxhdF91X2xpbSwgcFtjdW1zdW0oZmxhdF9wb3N0L25fZGF0YSkgPiAwLjk3NV1bMV0pCiAgICBiZXRhX2xfbGltICAgPSBjKGJldGFfbF9saW0sICBwW2N1bXN1bShiZXRhX3Bvc3Qvbl9kYXRhKSA+IDAuMDI1XVsxXSkKICAgIGJldGFfdV9saW0gID0gYyhiZXRhX3VfbGltLCBwW2N1bXN1bShiZXRhX3Bvc3Qvbl9kYXRhKSA+IDAuOTc1XVsxXSkKCiAgICAjYW5kIHVzZSB0aGUgZm91bmQgcG9zdGVyaW9ycyBhcyBwcmlvcnMgZm9yIHRoZSBuZXh0IHN0ZXAgCiAgICBmbGF0X3ByaW9yID0gZmxhdF9wb3N0CiAgICBiZXRhX3ByaW9yID0gYmV0YV9wb3N0CiAgICAKfQoKCmBgYAoKYGBge3IgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcud2lkdGg9NyAsIGZpZy5oZWlnaHQ9NX0KCmRmX3NlcSA9IGRhdGEuZnJhbWUoCiAgICB4ICAgICAgICAgID0gYyhwLCBwKSwKICAgIHBvc3RlcmlvciAgPSBjKGZsYXRfcG9zdCwgYmV0YV9wb3N0KSwKICAgIGxhYmVsICAgICAgPSBjKHJlcCgiVW5pZm9ybSIsIG5fZGF0YSksIHJlcCgiQmV0YSIsIG5fZGF0YSkpCikKCgpwbG90X3Bvc3RfcyA8LSBnZ3Bsb3QoZGF0YT1kZl9zZXEgLCBhZXMoeD14LCB5PXBvc3RlcmlvcikpICsgdGhlbWVfbGluZWRyYXcoKSArICAgCiAgZ2VvbV9saW5lKCBhZXMoY29sb3I9bGFiZWwpLCBsd2Q9MS41KSArCiAgICAgIGdndGl0bGUoIlBvc3RlcmlvciBGdW5jdGlvbnMgLSBTZXF1ZW50aWFsIEFuYWx5c2lzIikrICAgICAgICAgICAgICAKICAgICAgICAgIGxhYnMoeD0ncCcsIHk9J1Byb2JhYmlsaXR5IERlbnNpdHknKSArCiAgICAgICAgICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKcGxvdF9wb3N0X3MKCmBgYAoKYGBge3IgZmlnLmFsaWduPSJjZW50ZXIiLCBmaWcud2lkdGg9NyAsIGZpZy5oZWlnaHQ9NX0KCmRmX0NJID0gZGF0YS5mcmFtZSgKICAgIHggPSByZXAoMTpuX3RpbWVzLCAyKSwKICAgIHkgPSBjKG1vc3RfcHJvYl9mbGF0LCBtb3N0X3Byb2JfYmV0YSksCiAgICB5X21heCA9IGMoZmxhdF91X2xpbSwgYmV0YV91X2xpbSksCiAgICB5X21pbiA9IGMoZmxhdF9sX2xpbSwgYmV0YV9sX2xpbSksCiAgICBsYWJlbCA9IGMocmVwKCJGbGF0Iiwgbl90aW1lcyksIHJlcCgiQmV0YSIsIG5fdGltZXMpKQopCgpwbG90X0NJMyA8LSBnZ3Bsb3QoZGF0YT1kZl9DSSwgYWVzKHg9eCwgeT15LCB5bWF4PXlfbWF4LCBtaW49eV9taW4sIGZpbGw9bGFiZWwsIGxpbmV0eXBlPWxhYmVsLCBjb2xvcj1sYWJlbCkpICsgICAKICAgICB0aGVtZV9saW5lZHJhdygpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgIGdlb21fbGluZShhZXMoY29sb3I9bGFiZWwpLCBsd2Q9MS41KSArICAgIAogICAgIGdlb21fcmliYm9uKGFscGhhPTAuMykgKyAKICAKICAgICAgZ2d0aXRsZSgiU3VtbWFyeSBwbG90IC0gc2VxdWVudGlhbCBhbmFseXNpcyIpICsKICAgICAgbGFicyh4bGFiPSdTdGVwJywgeWxhYj0nTW9zdCBQcm9iYWJsZSBWYWx1ZSBmb3IgcCcsCiAgICAgICAgICAgc3VidGl0bGU9IkZpbGxlZCByZWdpb25zIHJlcHJlc2VudCB0aGUgOTUlIGNyZWRpYmlsaXR5IGludGVydmFsIikgKwogIAogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MjIpLAogICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLCAKICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTApLAogICAgICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoKcGxvdF9DSTMKCgpgYGAKCg==